<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="zh-CN" xml:lang="zh-CN" xmlns="http://www.w3.org/1999/xhtml">
 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<head>
<title>tree demo</title>
</head>
	
<style>
	.node {
		cursor: pointer;
	}

	.node circle {
	  fill: #fff;
	  stroke: steelblue;
	  stroke-width: 3px;
	}

	.node text {
	  font: 12px sans-serif;
	}

	.link {
	  fill: none;
	  stroke: #ccc;
	  stroke-width: 2px;
	}
</style>
 
 
<body>
	  <script src="../test/js/d3.v5.min.js" charset="utf-8"></script> 
<!--<script src="http://d3js.org/d3.v5.min.js" charset="utf-8"></script>  -->
<script>
 
// 嗯，这是最初的数据。
var treeData = {
  name: "Top Level",
  children: [
    {
      name: "Level 2: A",
      children: [{ name: "Son of A" }, { name: "Daughter of A" }]
    },
    { name: "Level 2: B" }
  ]
};
// 设置图表的宽高和Margin
var margin = { top: 20, right: 90, bottom: 30, left: 90 },
  width = 960 - margin.left - margin.right,
  height = 500 - margin.top - margin.bottom;
var svg = d3
  .select("body")
  // 在页面的body里添加svg对象
  .append("svg")
  // 设置svg宽高
  .attr("width", width + margin.right + margin.left)
  .attr("height", height + margin.top + margin.bottom)
  // 在svg里添加group元素
  .append("g")
  // 将group放置在左上方
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var i = 0,
  duration = 750,
  root;
// 定义Tree层级，并设置宽高
var treemap = d3.tree().size([height, width]);
// 计算父节点、字节点、高度和深度（parent, children, height, depth）
root = d3.hierarchy(treeData, function(d) {
  return d.children;
});
// 设置第一个元素的初始位置
root.x0 = height / 2;
root.y0 = 0;
// 第二层以上元素收起
root.children.forEach(collapse);
// 更新节点状态
update(root);
// collapse方法，用于切换子节点的展开和收起状态
function collapse(d) {
  if (d.children) {
    d._children = d.children;
    d._children.forEach(collapse);
    d.children = null;
  }
}
function update(source) {
  // 设置节点的x、y位置信息
  var treeData = treemap(root);
  // 计算新的Tree层级
  var nodes = treeData.descendants(),
    links = treeData.descendants().slice(1);
  // 设置每个同级节点间的y间距为180
  nodes.forEach(function(d) {
    d.y = d.depth * 180;
  });
  // ****************** 节点部分操作 ***************************
  // 给节点添加id，用于选择集索引
  var node = svg.selectAll("g.node").data(nodes, function(d) {
    return d.id || (d.id = ++i);
  });
  // 添加enter操作，添加类名为node的group元素
  var nodeEnter = node
    .enter()
    .append("g")
    .attr("class", "node")
    // 默认位置为当前父节点的位置
    .attr("transform", function(d) {
      return "translate(" + source.y0 + "," + source.x0 + ")";
    })
    // 给每个新加的节点绑定click事件
    .on("click", click);
  // 给每个新加的group元素添加cycle元素
  nodeEnter
    .append("circle")
    .attr("class", "node")
    .attr("r", 1e-6)
    // 如果元素有子节点，且为收起状态，则填充浅蓝色
    .style("fill", function(d) {
      return d._children ? "lightsteelblue" : "#fff";
    });
  // 给每个新加的group元素添加文字说明
  nodeEnter
    .append("text")
    .attr("dy", ".35em")
    .attr("x", function(d) {
      return d.children || d._children ? -13 : 13;
    })
    .attr("text-anchor", function(d) {
      return d.children || d._children ? "end" : "start";
    })
    .text(function(d) {
      return d.data.name;
    });
  // 获取update集
  var nodeUpdate = nodeEnter.merge(node);
  // 设置节点的位置变化，添加过渡动画效果
  nodeUpdate
    .transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + d.y + "," + d.x + ")";
    });
  // 更新节点的属性和样式
  nodeUpdate
    .select("circle.node")
    .attr("r", 10)
    .style("fill", function(d) {
      return d._children ? "lightsteelblue" : "#fff";
    })
    .attr("cursor", "pointer");
  // 获取exit操作
  var nodeExit = node
    .exit()
    // 添加过渡动画
    .transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + source.y + "," + source.x + ")";
    })
    // 移除元素
    .remove();
  // exit集中节点的cycle元素尺寸变为0
  nodeExit.select("circle").attr("r", 1e-6);
  // exit集中节点的text元素可见度降为0
  nodeExit.select("text").style("fill-opacity", 1e-6);
  // ****************** 连接线部分操作 ***************************
  // 更新数据
  var link = svg.selectAll("path.link").data(links, function(d) {
    return d.id;
  });
  // 添加enter操作，添加类名为link的path元素
  var linkEnter = link
    .enter()
    .insert("path", "g")
    .attr("class", "link")
    // 默认位置为当前父节点的位置
    .attr("d", function(d) {
      var o = { x: source.x0, y: source.y0 };
      return diagonal(o, o);
    });
  // 获取update集
  var linkUpdate = linkEnter.merge(link);
  // 更新添加过渡动画
  linkUpdate
    .transition()
    .duration(duration)
    .attr("d", function(d) {
      return diagonal(d, d.parent);
    });
  // 获取exit集
  var linkExit = link
    .exit()
    // 设置过渡动画
    .transition()
    .duration(duration)
    .attr("d", function(d) {
      var o = { x: source.x, y: source.y };
      return diagonal(o, o);
    })
    // 移除link
    .remove();
  // 为动画过渡保存旧的位置
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
  // 添加贝塞尔曲线的path，衔接与父节点和子节点间
  function diagonal(s, d) {
    path = `M ${s.y} ${s.x}
            C ${(s.y + d.y) / 2} ${s.x},
              ${(s.y + d.y) / 2} ${d.x},
              ${d.y} ${d.x}`;
    return path;
  }
  // 当点击时，切换children，同时用_children来保存原子节点信息
  function click(d) {
    if (d.children) {
      d._children = d.children;
      d.children = null;
    } else {
      d.children = d._children;
      d._children = null;
    }
    update(d);
  }
}
</script>
 
</body>
</html>

